/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ets_ability_manager_utils.h"

#include "ani_common_util.h"
#include "ani_common_want.h"
#include "ani_enum_convert.h"

namespace OHOS {
namespace AbilityManagerEts {
constexpr const char *CLASSNAME_ARRAY = "Lescompat/Array;";
constexpr const char *SET_OBJECT_VOID_SIGNATURE = "ILstd/core/Object;:V";
constexpr const char *CLASSNAME_ABILITY_RRUNNINGINFO = "Lapplication/AbilityRunningInfo/AbilityRunningInfoImpl;";
constexpr const char *ABILITY_STATE_ENUM_NAME = "L@ohos/app/ability/abilityManager/abilityManager/AbilityState;";
constexpr const char *CLASSNAME_EXTENSION_RUNNINGINFO = "Lapplication/ExtensionRunningInfo/ExtensionRunningInfoInner;";
constexpr const char *EXTENSION_ABILITY_TYPE_ENUM_NAME
    = "L@ohos/bundle/bundleManager/bundleManager/ExtensionAbilityType;";

bool WrapAbilityRunningInfoArray(
    ani_env *env, ani_object &arrayObj, const std::vector<AAFwk::AbilityRunningInfo> &infos)
{
    TAG_LOGD(AAFwkTag::ABILITYMGR, "WrapAbilityRunningInfoArray");
    if (env == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null env");
        return false;
    }
    ani_class arrayCls = nullptr;
    ani_method arrayCtor = nullptr;
    ani_status status = ANI_ERROR;
    if ((status = env->FindClass(CLASSNAME_ARRAY, &arrayCls)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "status : %{public}d", status);
        return false;
    }
    if (arrayCls == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null arrayCls");
        return false;
    }
    if ((status = env->Class_FindMethod(arrayCls, "<ctor>", "I:V", &arrayCtor)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "status : %{public}d", status);
        return false;
    }
    if (arrayCtor == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null arrayCtor");
        return false;
    }
    if ((status = env->Object_New(arrayCls, arrayCtor, &arrayObj, infos.size())) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "status : %{public}d", status);
        return false;
    }
    if (arrayObj == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null arrayObjs");
        return false;
    }
    for (size_t i = 0; i < infos.size(); i++) {
        ani_object infoObj = nullptr;
        if (!WrapAbilityRunningInfo(env, infoObj, infos[i])) {
            TAG_LOGE(AAFwkTag::ABILITYMGR, "WrapAbilityRunningInfo failed");
            return false;
        }
        status = env->Object_CallMethodByName_Void(arrayObj, "$_set", SET_OBJECT_VOID_SIGNATURE, i, infoObj);
        if (status != ANI_OK) {
            TAG_LOGE(AAFwkTag::ABILITYMGR, "status : %{public}d", status);
            return false;
        }
    }
    return true;
}

bool WrapAbilityRunningInfo(ani_env *env, ani_object &infoObj, const AAFwk::AbilityRunningInfo &info)
{
    TAG_LOGD(AAFwkTag::ABILITYMGR, "WrapAbilityRunningInfo");
    if (env == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null env");
        return false;
    }
    ani_object elementNameObj = WrapElementName(env, info.ability);
    if (elementNameObj == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "WrapElementName failed");
        return false;
    }
    ani_class cls;
    ani_status status = ANI_ERROR;
    if ((status = env->FindClass(CLASSNAME_ABILITY_RRUNNINGINFO, &cls)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "findClass failed, status: %{public}d", status);
        return false;
    }
    if (cls == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null cls");
        return false;
    }
    ani_method method = nullptr;
    if ((status = env->Class_FindMethod(cls, "<ctor>", ":V", &method)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "findMethod failed, status: %{public}d", status);
        return false;
    }
    if (method == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null method");
        return false;
    }
    if ((status = env->Object_New(cls, method, &infoObj)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "Object_New failed, status: %{public}d", status);
        return false;
    }
    if (infoObj == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null infoObj");
        return false;
    }
    if ((status = env->Object_SetPropertyByName_Ref(infoObj, "ability", elementNameObj)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set ability failed, status: %{public}d", status);
        return false;
    }
    return WrapAbilityRunningInfoInner(env, infoObj, info, cls);
}

bool WrapAbilityRunningInfoInner(
    ani_env *env, ani_object &infoObj, const AAFwk::AbilityRunningInfo &info, ani_class cls)
{
    ani_status status = ANI_ERROR;
    if ((status = env->Object_SetPropertyByName_Int(infoObj, "pid", info.pid)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set pid failed, status: %{public}d", status);
        return false;
    }
    if ((status = env->Object_SetPropertyByName_Int(infoObj, "uid", info.uid)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set uid failed, status: %{public}d", status);
        return false;
    }
    if (!AppExecFwk::SetFieldStringByName(env, cls, infoObj, "processName", info.processName)) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set processName failed");
        return false;
    }
    if ((status = env->Object_SetPropertyByName_Long(infoObj, "startTime", info.startTime)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set processName failed, status: %{public}d", status);
        return false;
    }
    ani_enum_item abilityStateItem {};
    OHOS::AAFwk::AniEnumConvertUtil::EnumConvert_NativeToEts(
        env, ABILITY_STATE_ENUM_NAME, info.abilityState, abilityStateItem);
    if ((status = env->Object_SetPropertyByName_Ref(infoObj, "abilityState", abilityStateItem)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set abilityState failed, status: %{public}d", status);
        return false;
    }
    return true;
}

bool WrapArrayString(ani_env *env, ani_object &arrayObj, const std::vector<std::string> &values)
{
    TAG_LOGD(AAFwkTag::ABILITYMGR, "WrapArrayString");
    if (env == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null env");
        return false;
    }
    ani_class arrayCls = nullptr;
    ani_method arrayCtor = nullptr;
    ani_string aniStr = nullptr;
    ani_status status = ANI_ERROR;
    if ((status = env->FindClass(CLASSNAME_ARRAY, &arrayCls)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "findClass failed, status : %{public}d", status);
        return false;
    }
    if (arrayCls == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null arrayCls");
        return false;
    }
    if ((status = env->Class_FindMethod(arrayCls, "<ctor>", "I:V", &arrayCtor)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "findMethod failed, status : %{public}d", status);
        return false;
    }
    if (arrayCtor == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null arrayCtor");
        return false;
    }
    if ((status = env->Object_New(arrayCls, arrayCtor, &arrayObj, values.size())) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "Object_New failed, status : %{public}d", status);
        return false;
    }
    if (arrayObj == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null arrayObj");
        return false;
    }
    for (size_t i = 0; i < values.size(); i++) {
        status = env->String_NewUTF8(values[i].c_str(), values[i].size(), &aniStr);
        if (status != ANI_OK) {
            TAG_LOGE(AAFwkTag::ABILITYMGR, "String_NewUTF8 failed, status : %{public}d", status);
            return false;
        }
        status = env->Object_CallMethodByName_Void(arrayObj, "$_set", "ILstd/core/Object;:V", i, aniStr);
        if (status != ANI_OK) {
            TAG_LOGE(AAFwkTag::ABILITYMGR, "Object_CallMethodByName_Void failed, status : %{public}d", status);
            return false;
        }
    }
    return true;
}

bool WrapExtensionRunningInfoArray(
    ani_env *env, ani_object &arrayObj, const std::vector<AAFwk::ExtensionRunningInfo> &infos)
{
    TAG_LOGD(AAFwkTag::ABILITYMGR, "WrapExtensionRunningInfoArray");
    if (env == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null env");
        return false;
    }
    ani_class arrayCls = nullptr;
    ani_method arrayCtor = nullptr;
    ani_status status = ANI_ERROR;
    if ((status = env->FindClass(CLASSNAME_ARRAY, &arrayCls)) != ANI_OK || arrayCls == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "FindClass failed, status : %{public}d", status);
        return false;
    }
    if ((status = env->Class_FindMethod(arrayCls, "<ctor>", "I:V", &arrayCtor)) != ANI_OK || arrayCtor == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "Class_FindMethod failed, status : %{public}d", status);
        return false;
    }
    if ((status = env->Object_New(arrayCls, arrayCtor, &arrayObj, infos.size())) != ANI_OK || arrayObj == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "Object_New failed, status : %{public}d", status);
        return false;
    }
    for (size_t i = 0; i < infos.size(); i++) {
        ani_object infoObj = nullptr;
        if (!WrapExtensionRunningInfo(env, infoObj, infos[i])) {
            TAG_LOGE(AAFwkTag::ABILITYMGR, "WrapExtensionRunningInfo failed");
            return false;
        }
        status = env->Object_CallMethodByName_Void(arrayObj, "$_set", SET_OBJECT_VOID_SIGNATURE, i, infoObj);
        if (status != ANI_OK) {
            TAG_LOGE(AAFwkTag::ABILITYMGR, "Object_CallMethodByName_Void failed, status : %{public}d", status);
            return false;
        }
    }
    return true;
}

bool WrapExtensionRunningInfo(ani_env *env, ani_object &infoObj, const AAFwk::ExtensionRunningInfo &info)
{
    TAG_LOGD(AAFwkTag::ABILITYMGR, "WrapExtensionRunningInfo");
    if (env == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null env");
        return false;
    }
    ani_object elementNameObj = WrapElementName(env, info.extension);
    if (elementNameObj == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "WrapElementName failed");
        return false;
    }
    ani_class cls = nullptr;
    ani_status status = ANI_ERROR;
    if ((status = env->FindClass(CLASSNAME_EXTENSION_RUNNINGINFO, &cls)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "findClass failed, status: %{public}d", status);
        return false;
    }
    if (cls == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null cls");
        return false;
    }
    ani_method method = nullptr;
    if ((status = env->Class_FindMethod(cls, "<ctor>", ":V", &method)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "findMethod failed, status: %{public}d", status);
        return false;
    }
    if (method == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null method");
        return false;
    }
    if ((status = env->Object_New(cls, method, &infoObj)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "Object_New failed, status: %{public}d", status);
        return false;
    }
    if (infoObj == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null infoObj");
        return false;
    }
    if ((status = env->Object_SetPropertyByName_Ref(infoObj, "extension", elementNameObj)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set extension failed, status: %{public}d", status);
        return false;
    }
    return WrapExtensionRunningInfoInner(env, infoObj, info, cls);
}

bool WrapExtensionRunningInfoInner(
    ani_env *env, ani_object &infoObj, const AAFwk::ExtensionRunningInfo &info, ani_class cls)
{
    ani_status status = ANI_ERROR;
    if ((status = env->Object_SetPropertyByName_Int(infoObj, "pid", info.pid)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set pid failed, status: %{public}d", status);
        return false;
    }
    if ((status = env->Object_SetPropertyByName_Int(infoObj, "uid", info.uid)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set uid failed, status: %{public}d", status);
        return false;
    }
    if (!AppExecFwk::SetFieldStringByName(env, cls, infoObj, "processName", info.processName)) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set processName failed");
        return false;
    }
    if ((status = env->Object_SetPropertyByName_Long(infoObj, "startTime", info.startTime)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set startTimee failed, status: %{public}d", status);
        return false;
    }
    ani_object stringArray = nullptr;
    WrapArrayString(env, stringArray, info.clientPackage);
    if (((status = env->Object_SetPropertyByName_Ref(
        infoObj, "clientPackage", reinterpret_cast<ani_ref>(stringArray))) != ANI_OK)) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set clientPackage failed, status: %{public}d", status);
        return false;
    }
    ani_enum_item typeItem = nullptr;
    OHOS::AAFwk::AniEnumConvertUtil::EnumConvert_NativeToEts(
        env, EXTENSION_ABILITY_TYPE_ENUM_NAME, info.type, typeItem);
    if ((status = env->Object_SetPropertyByName_Ref(infoObj, "type", typeItem)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "set type failed, status: %{public}d", status);
        return false;
    }
    return true;
}

bool SetAbilityStateData(ani_env *env, ani_object object, const AbilityStateData &abilityStateData)
{
    if (env == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null env");
        return false;
    }
    ani_status status = ANI_OK;
    if ((status = env->Object_SetFieldByName_Ref(object, "moduleName",
        OHOS::AppExecFwk::GetAniString(env, abilityStateData.moduleName))) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "moduleName failed status:%{public}d", status);
        return false;
    }
    if ((status = env->Object_SetFieldByName_Ref(object, "bundleName",
        OHOS::AppExecFwk::GetAniString(env, abilityStateData.bundleName))) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "bundleName failed status:%{public}d", status);
        return false;
    }
    if ((status = env->Object_SetFieldByName_Ref(object, "abilityName",
        OHOS::AppExecFwk::GetAniString(env, abilityStateData.abilityName))) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "abilityName failed status:%{public}d", status);
        return false;
    }
    if ((status = env->Object_SetFieldByName_Int(object, "pid", static_cast<int32_t>(abilityStateData.pid)))
        != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "pid failed status:%{public}d", status);
        return false;
    }
    if ((status = env->Object_SetFieldByName_Int(object, "uid", abilityStateData.uid)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "uid failed status:%{public}d", status);
        return false;
    }
    if ((status = env->Object_SetFieldByName_Int(object, "state", abilityStateData.abilityState)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "state failed status:%{public}d", status);
        return false;
    }
    if ((status = env->Object_SetFieldByName_Int(object, "abilityType", abilityStateData.abilityType)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "abilityType failed status:%{public}d", status);
        return false;
    }
    if ((status = env->Object_SetFieldByName_Boolean(object, "isAtomicService",
        abilityStateData.isAtomicService)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "isAtomicService failed status:%{public}d", status);
        return false;
    }
    if ((status = env->Object_SetFieldByName_Ref(object, "appCloneIndex",
        OHOS::AppExecFwk::CreateInt(env, abilityStateData.appCloneIndex))) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "appCloneIndex failed status:%{public}d", status);
        return false;
    }
    return true;
}

ani_object WrapAbilityStateData(ani_env *env, const AbilityStateData &abilityStateData)
{
    ani_class cls {};
    ani_status status = ANI_ERROR;
    ani_method method {};
    ani_object object = nullptr;
    if (env == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null env");
        return nullptr;
    }
    if ((status = env->FindClass("Lapplication/AbilityStateData/AbilityStateData;", &cls)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "find class failed status : %{public}d", status);
        return nullptr;
    }
    if (cls == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null cls");
        return nullptr;
    }
    if ((status = env->Class_FindMethod(cls, "<ctor>", ":V", &method)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "find ctor failed status : %{public}d", status);
        return nullptr;
    }
    if ((status = env->Object_New(cls, method, &object)) != ANI_OK) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "Object_New AppStateData failed status : %{public}d", status);
        return nullptr;
    }
    if (object == nullptr) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "null object");
        return nullptr;
    }
    if (!SetAbilityStateData(env, object, abilityStateData)) {
        TAG_LOGE(AAFwkTag::ABILITYMGR, "SetAppStateData failed");
        return nullptr;
    }
    return object;
}

} // namespace AbilityManagerEts
} // namespace OHOS
